/* * Copyright 2007, Plutext Pty Ltd. * * This file is part of Docx4all. Docx4all is free software: you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. Docx4all is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Docx4all. If not, see <http://www.gnu.org/licenses/>. */ package org.docx4all.ui.main; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Rectangle; import java.awt.datatransfer.Clipboard; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.event.MouseMotionListener; import java.beans.PropertyVetoException; import java.io.File; import java.util.ArrayList; import java.util.EventObject; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.Box; import javax.swing.JApplet; import javax.swing.JComponent; import javax.swing.JDesktopPane; import javax.swing.JEditorPane; import javax.swing.JInternalFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import javax.swing.WindowConstants; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; import javax.swing.plaf.basic.BasicInternalFrameUI; import javax.swing.text.AbstractDocument; import javax.swing.text.Document; import javax.swing.text.EditorKit; import javax.swing.text.MutableAttributeSet; import javax.swing.text.PlainDocument; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import net.sf.vfsjfilechooser.utils.VFSURIParser; import net.sf.vfsjfilechooser.utils.VFSUtils; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSystemException; import org.apache.commons.vfs.provider.webdav.WebdavFileObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.bounce.text.xml.XMLDocument; import org.bounce.text.xml.XMLEditorKit; import org.bounce.text.xml.XMLStyleConstants; import org.docx4all.datatransfer.TransferHandler; import org.docx4all.datatransfer.WordMLTransferable; import org.docx4all.script.FxScriptUIHelper; import org.docx4all.swing.WordMLTextPane; import org.docx4all.swing.text.DocumentElement; import org.docx4all.swing.text.FontManager; import org.docx4all.swing.text.WordMLDocument; import org.docx4all.swing.text.WordMLDocumentFilter; import org.docx4all.swing.text.WordMLEditorKit; import org.docx4all.ui.menu.ContentControlMenu; import org.docx4all.ui.menu.EditMenu; import org.docx4all.ui.menu.FileMenu; import org.docx4all.ui.menu.FormatMenu; import org.docx4all.ui.menu.HelpMenu; import org.docx4all.ui.menu.HyperlinkMenu; import org.docx4all.ui.menu.PlutextMenu; import org.docx4all.ui.menu.ReviewMenu; import org.docx4all.ui.menu.ViewMenu; import org.docx4all.ui.menu.WindowMenu; import org.docx4all.util.DocUtil; import org.docx4all.util.SwingUtil; import org.docx4all.xml.BodyML; import org.docx4all.xml.DocumentML; import org.docx4all.xml.ElementML; import org.docx4all.xml.SdtBlockML; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.jdesktop.application.ResourceMap; import org.jdesktop.application.SingleFrameApplication; import org.plutext.client.Mediator; /** * @author Jojada Tirtowidjojo - 13/11/2007 */ public class WordMLEditor extends SingleFrameApplication { private static Logger log = LoggerFactory.getLogger(WordMLEditor.class); private ViewManager _viewManager; private JDesktopPane _desktop; private Map<String, JInternalFrame> _iframeMap; private InternalFrameListener _internalFrameListener; private MouseMotionListener _titleBarMouseListener; private ToolBarStates _toolbarStates; private JApplet _applet; public static String TRANSFORMER_FACTORY_DEFAULT; public static void main(String[] args) { // // Whenever we flush Preferences on Linux, we need *not* to use // // our Xalan jar (which we need // javax.xml.transform.TransformerFactory tfactory = javax.xml.transform.TransformerFactory.newInstance(); // TRANSFORMER_FACTORY_DEFAULT = tfactory.getClass().getName(); // log.debug("Set TRANSFORMER_FACTORY_DEFAULT to " + TRANSFORMER_FACTORY_DEFAULT); // If we launch via JNLP with Java 6 JAXB (as opposed to the JAXB RI), // we trip up with access denied on RuntimePermission accessDeclaredMembers // (is this because NamespacePrefixMapperSunInternal extends com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper?). // This is despite the JNLP file seeking all-permissions. // Workaround: if (System.getSecurityManager()==null) { System.out.println("Initial SecurityManager: null" ); } else { System.out.println("Initial SecurityManager: " + System.getSecurityManager().getClass().getName() ); System.setSecurityManager(null); } launch(WordMLEditor.class, args); } @Override protected void startup() { log.info("preStartup()..."); preStartup(null); log.info("setting up createMenuBar"); getMainFrame().setJMenuBar(createMenuBar()); log.info("setting up createMainPanel"); show(createMainPanel()); log.info("startup() complete."); } void preStartup(JApplet applet) { _applet = applet; _viewManager = new ViewManager(); _iframeMap = new HashMap<String, JInternalFrame>(); log.info("setting up InternalFrameListener"); _internalFrameListener = new InternalFrameListener(); log.info("setting up TitleBarMouseListener"); _titleBarMouseListener = new TitleBarMouseListener(); log.info("setting up ToolBarStates"); _toolbarStates = new ToolBarStates(); Clipboard clipboard = getContext().getClipboard(); clipboard.addFlavorListener(_toolbarStates); //As a FlavorListener, _toolbarStates will ONLY be notified //when there is a DataFlavor change in Clipboard. //Therefore, make sure that toolbarStates' _isPasteEnable property //is initialised correctly. boolean available = clipboard.isDataFlavorAvailable(WordMLTransferable.STRING_FLAVOR) || clipboard.isDataFlavorAvailable(WordMLTransferable.WORDML_FRAGMENT_FLAVOR); _toolbarStates.setPasteEnabled(available); log.info("setting up VFSJFileChooser"); initVFSJFileChooser(); log.info("setting up WmlExitListener"); addExitListener(new WmlExitListener()); } /** * Initialises VFSJFileChooser default bookmark entry which has to be * a webdav folder pointed by Constants.VFSJFILECHOOSER_DEFAULT_FOLDER_URI * property. */ private void initVFSJFileChooser() { ResourceMap rm = getContext().getResourceMap(WordMLEditor.class); String uri = rm.getString(Constants.VFSJFILECHOOSER_DEFAULT_FOLDER_URI); System.out.println(Constants.VFSJFILECHOOSER_DEFAULT_FOLDER_URI + ":" + uri); if (uri != null && uri.length() > 0) { uri = uri.trim(); try { FileObject fo = VFSUtils.getFileSystemManager().resolveFile(uri); if (fo instanceof WebdavFileObject) { String name = rm.getString(Constants.VFSJFILECHOOSER_DEFAULT_WEBDAV_FOLDER_BOOKMARK_NAME); if (name == null || name.length() == 0) { name = "Default Webdav Folder"; } else { name = name.trim(); } VFSURIParser vup = new VFSURIParser(uri, false); org.docx4all.vfs.VFSUtil.addBookmarkEntry(name, vup); } } catch (FileSystemException exc) { exc.printStackTrace(); StringBuilder sb = new StringBuilder(); sb.append("Bad "); sb.append(Constants.VFSJFILECHOOSER_DEFAULT_FOLDER_URI); sb.append(" property."); throw new RuntimeException(sb.toString()); } } } //initVFSJFileChooser() public void closeAllInternalFrames() { List<JInternalFrame> list = getAllInternalFrames(); //Start from current editor's frame JInternalFrame currentFrame = getCurrentInternalFrame(); list.remove(currentFrame); list.add(0, currentFrame); for (final JInternalFrame iframe: list) { final Runnable disposeRunnable = new Runnable() { public void run() { iframe.dispose(); } }; if (getToolbarStates().isDocumentDirty(iframe)) { try { iframe.setSelected(true); iframe.setIcon(false); } catch (PropertyVetoException exc) { ;//ignore } int answer = showConfirmClosingInternalFrame(iframe, "internalframe.close"); if (answer == JOptionPane.CANCEL_OPTION) { break; } } SwingUtilities.invokeLater(disposeRunnable); } } public void closeInternalFrame(JInternalFrame iframe) { boolean canClose = true; if (getToolbarStates().isDocumentDirty(iframe)) { try { iframe.setSelected(true); iframe.setIcon(false); } catch (PropertyVetoException exc) { ;//ignore } int answer = showConfirmClosingInternalFrame(iframe, "internalframe.close"); canClose = (answer != JOptionPane.CANCEL_OPTION); } if (canClose) { WordMLTextPane editor = SwingUtil.getWordMLTextPane(iframe); if (editor != null) { editor.removeCaretListener(getToolbarStates()); editor.removeFocusListener(getToolbarStates()); editor.setTransferHandler(null); editor.getDocument().removeDocumentListener(getToolbarStates()); WordMLEditorKit editorKit = (WordMLEditorKit) editor.getEditorKit(); editorKit.removeInputAttributeListener(getToolbarStates()); editor.getWordMLEditorKit().deinstall(editor); } iframe.dispose(); } } public void createInternalFrame(FileObject f) { if (f == null) { return; } log.info(VFSUtils.getFriendlyName(f.getName().getURI())); JInternalFrame iframe = _iframeMap.get(f.getName().getURI()); if (iframe != null) { iframe.setVisible(true); } else { iframe = new JInternalFrame(f.getName().getBaseName(), true, true, true, true); iframe.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); iframe.addInternalFrameListener(_internalFrameListener); iframe.addInternalFrameListener(_toolbarStates); iframe.addPropertyChangeListener(WindowMenu.getInstance()); if (iframe.getUI() instanceof BasicInternalFrameUI) { BasicInternalFrameUI ui = (BasicInternalFrameUI) iframe.getUI(); javax.swing.JComponent northPane = ui.getNorthPane(); if (northPane==null) { // Happens on Mac OSX: Google for "osx java getNorthPane" // Fix is from it.businesslogic.ireport.gui.JMDIFrame javax.swing.plaf.basic.BasicInternalFrameUI aUI = new javax.swing.plaf.basic.BasicInternalFrameUI(iframe); iframe.setUI(aUI); // Try again ui = (BasicInternalFrameUI) iframe.getUI(); northPane = ((javax.swing.plaf.basic.BasicInternalFrameUI)ui).getNorthPane(); } northPane.addMouseMotionListener(_titleBarMouseListener); } JEditorPane editorView = createEditorView(f); JPanel panel = FxScriptUIHelper.getInstance().createEditorPanel(editorView); iframe.getContentPane().add(panel); iframe.pack(); _desktop.add(iframe); editorView.requestFocusInWindow(); editorView.select(0,0); String filePath = f.getName().getURI(); iframe.putClientProperty(WordMLDocument.FILE_PATH_PROPERTY, filePath); _iframeMap.put(filePath, iframe); iframe.show(); } try { iframe.setSelected(true); iframe.setIcon(false); iframe.setMaximum(true); } catch (PropertyVetoException exc) { // do nothing } } public void tileLayout(String filePath1, String filePath2) { if (_iframeMap.containsKey(filePath1) && _iframeMap.containsKey(filePath2)) { JInternalFrame[] frames = { _iframeMap.get(filePath1), _iframeMap.get(filePath2) }; WindowMenu.tileLayout(frames); } } public void updateInternalFrame(FileObject oldFile, FileObject newFile) { if (oldFile.equals(newFile)) { return; } String fileUri = oldFile.getName().getURI(); JInternalFrame iframe = _iframeMap.remove(fileUri); if (iframe != null) { fileUri = newFile.getName().getURI(); iframe.putClientProperty(WordMLDocument.FILE_PATH_PROPERTY, fileUri); iframe.setTitle(newFile.getName().getBaseName()); } } public String getPlutextWebdavUrlKeyword() { ResourceMap rm = getContext().getResourceMap(WordMLEditor.class); return rm.getString(Constants.PLUTEXT_WEBDAV_URL_KEYWORD); } public JDesktopPane getDesktopPane() { return _desktop; } public ToolBarStates getToolbarStates() { return _toolbarStates; } public JInternalFrame getCurrentInternalFrame() { return _toolbarStates.getCurrentInternalFrame(); } public JEditorPane getCurrentEditor() { return _toolbarStates.getCurrentEditor(); } public JEditorPane getView(String viewTabTitle) { if (getCurrentInternalFrame() != null) { return getCurrentViewManager().getView(viewTabTitle); } return null; } public List<JInternalFrame> getAllInternalFrames() { return new ArrayList<JInternalFrame>(_iframeMap.values()); } public String getEditorViewTabTitle() { ResourceMap rm = getContext().getResourceMap(WordMLEditor.class); return rm.getString(Constants.EDITOR_VIEW_TAB_TITLE); } public String getSourceViewTabTitle() { ResourceMap rm = getContext().getResourceMap(WordMLEditor.class); return rm.getString(Constants.SOURCE_VIEW_TAB_TITLE); } public String getContentControlHistoryViewTabTitle() { ResourceMap rm = getContext().getResourceMap(WordMLEditor.class); return rm.getString(Constants.CONTENT_CONTROL_HISTORY_VIEW_TAB_TITLE); } public String getRecentChangesViewTabTitle() { ResourceMap rm = getContext().getResourceMap(WordMLEditor.class); return rm.getString(Constants.RECENT_CHANGES_VIEW_TAB_TITLE); } public String getUntitledFileName() { ResourceMap rm = getContext().getResourceMap(WordMLEditor.class); String filename = rm.getString(Constants.UNTITLED_FILE_NAME); if (filename == null || filename.length() == 0) { filename = "Untitled"; } return filename; } public Frame getWindowFrame() { if (_applet == null) { return getMainFrame(); } Component c = _applet; while (c != null && !(c instanceof Frame)) { c = c.getParent(); } return (Frame) c; } public JMenuBar getJMenuBar() { if (_applet == null) { return getMainFrame().getJMenuBar(); } return _applet.getJMenuBar(); } public int showConfirmDialog( String title, String message, int optionType, int messageType) { return JOptionPane.showConfirmDialog( getWindowFrame(), message, title, optionType, messageType); } public int showConfirmDialog( String title, String message, int optionType, int messageType, Object[] options, Object initialValue) { return JOptionPane.showOptionDialog( getWindowFrame(), message, title, optionType, messageType, null, options, initialValue); } public void showMessageDialog(String title, String message, int optionType) { JOptionPane.showMessageDialog(getWindowFrame(), message, title, optionType); } public void showViewInTab(final String tabTitle) { if (getCurrentInternalFrame() != null) { Cursor origCursor = getMainFrame().getCursor(); getMainFrame().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); getCurrentViewManager().showViewTab(tabTitle); getMainFrame().setCursor(origCursor); } } public void closeViewTab(String tabTitle) { if (getCurrentInternalFrame() != null) { getCurrentViewManager().closeViewTab(tabTitle); } } private JEditorPane createSourceView(WordMLTextPane editorView) { //Create the Source View JEditorPane sourceView = new JEditorPane(); MutableAttributeSet attrs = new SimpleAttributeSet(); StyleConstants.setFontFamily( attrs, FontManager.getInstance().getSourceViewFontFamilyName()); StyleConstants.setFontSize( attrs, FontManager.getInstance().getSourceViewFontSize()); // TODO - only do this if the font is available. Font font = new Font ( "Arial Unicode MS", Font.PLAIN, 12 ) ; System.out.println( font.getFamily() ); System.out.println( font.getFontName() ); System.out.println( font.getPSName() ); sourceView.setFont(font); //sourceView.setFont(FontManager.getInstance().getFontInAction(attrs)); sourceView.setContentType("text/xml; charset=UTF-16"); // Instantiate a XMLEditorKit with wrapping enabled. XMLEditorKit kit = new XMLEditorKit( true); // Set the wrapping style. kit.setWrapStyleWord( true); sourceView.setEditorKit( kit); WordMLDocument editorViewDoc = (WordMLDocument) editorView.getDocument(); try { editorViewDoc.readLock(); editorView.getWordMLEditorKit().saveCaretText(); DocumentElement elem = (DocumentElement) editorViewDoc.getDefaultRootElement(); WordprocessingMLPackage wmlPackage = ((DocumentML) elem.getElementML()).getWordprocessingMLPackage(); String filePath = (String) editorView.getDocument().getProperty(WordMLDocument.FILE_PATH_PROPERTY); //Do not include the last paragraph which is an extra paragraph. elem = (DocumentElement) elem.getElement(elem.getElementCount() - 1); ElementML paraML = elem.getElementML(); ElementML bodyML = paraML.getParent(); paraML.delete(); Document doc = DocUtil.read(sourceView, wmlPackage); doc.putProperty(WordMLDocument.FILE_PATH_PROPERTY, filePath); doc.putProperty(WordMLDocument.WML_PACKAGE_PROPERTY, wmlPackage); doc.addDocumentListener(getToolbarStates()); //Below are the properties used by bounce.jar library //See http://www.edankert.com/bounce/xmleditorkit.html doc.putProperty(PlainDocument.tabSizeAttribute, new Integer(4)); doc.putProperty(XMLDocument.AUTO_INDENTATION_ATTRIBUTE, Boolean.TRUE); doc.putProperty(XMLDocument.TAG_COMPLETION_ATTRIBUTE, Boolean.TRUE); //Remember to put 'paraML' as last paragraph bodyML.addChild(paraML); } finally { editorViewDoc.readUnlock(); } kit.setStyle( XMLStyleConstants.ATTRIBUTE_NAME, new Color( 255, 0, 0), Font.PLAIN); sourceView.addFocusListener(getToolbarStates()); //sourceView.setDocument(doc); sourceView.putClientProperty(Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG, Boolean.TRUE); return sourceView; } private JEditorPane createContentControlHistoryView(WordMLTextPane editorView) { WordMLTextPane theView = new WordMLTextPane(); editorView.saveCaretText(); int pos = editorView.getCaretPosition(); WordMLDocument editorViewDoc = (WordMLDocument) editorView.getDocument(); Mediator plutextClient = editorView.getWordMLEditorKit().getPlutextClient(); try { editorViewDoc.readLock(); DocumentElement elem = (DocumentElement) editorViewDoc.getSdtBlockMLElement(pos); SdtBlockML sdt = (SdtBlockML) elem.getElementML(); String sdtId = sdt.getSdtProperties().getPlutextId(); plutextClient.startSession(); WordprocessingMLPackage wp = plutextClient.getVersionHistory(sdtId); org.docx4j.wml.Document wmlDoc = (org.docx4j.wml.Document) wp.getMainDocumentPart().getJaxbElement(); WordMLDocument historyDoc = (WordMLDocument) theView.getDocument(); historyDoc.setDocumentFilter(new WordMLDocumentFilter()); historyDoc.replaceBodyML(new BodyML(wmlDoc.getBody())); } catch (Exception exc) { exc.printStackTrace(); } finally { plutextClient.endSession(); editorViewDoc.readUnlock(); } theView.setEditable(false); theView.addFocusListener(getToolbarStates()); return theView; } private JEditorPane createRecentChangesView(WordMLTextPane editorView) { WordMLTextPane theView = new WordMLTextPane(); editorView.saveCaretText(); Mediator plutextClient = editorView.getWordMLEditorKit().getPlutextClient(); try { plutextClient.startSession(); WordprocessingMLPackage wp = plutextClient.getRecentChangesReport(); org.docx4j.wml.Document wmlDoc = (org.docx4j.wml.Document) wp.getMainDocumentPart().getJaxbElement(); WordMLDocument doc = (WordMLDocument) theView.getDocument(); doc.setDocumentFilter(new WordMLDocumentFilter()); doc.replaceBodyML(new BodyML(wmlDoc.getBody())); } catch (Exception exc) { exc.printStackTrace(); } finally { plutextClient.endSession(); } theView.setEditable(false); theView.addFocusListener(getToolbarStates()); return theView; } private JEditorPane createEditorView(FileObject f) { String fileUri = f.getName().getURI(); WordMLTextPane editorView = new WordMLTextPane(); editorView.addFocusListener(_toolbarStates); editorView.addCaretListener(_toolbarStates); editorView.setTransferHandler(new TransferHandler()); WordMLEditorKit editorKit = (WordMLEditorKit) editorView.getEditorKit(); editorKit.addInputAttributeListener(_toolbarStates); WordMLDocument doc = null; try { if (f.exists()) { doc = editorKit.read(f); } } catch (Exception exc) { exc.printStackTrace(); ResourceMap rm = getContext().getResourceMap(); String title = rm.getString(Constants.INIT_EDITOR_VIEW_IO_ERROR_DIALOG_TITLE); StringBuffer msg = new StringBuffer(); msg.append(rm.getString(Constants.INIT_EDITOR_VIEW_IO_ERROR_MESSAGE)); msg.append(Constants.NEWLINE); msg.append(VFSUtils.getFriendlyName(fileUri)); showMessageDialog(title, msg.toString(), JOptionPane.ERROR_MESSAGE); doc = null; } if (doc == null) { doc = (WordMLDocument) editorKit.createDefaultDocument(); } doc.putProperty(WordMLDocument.FILE_PATH_PROPERTY, fileUri); doc.addDocumentListener(_toolbarStates); doc.setDocumentFilter(new WordMLDocumentFilter()); editorView.setDocument(doc); editorView.putClientProperty(Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG, Boolean.TRUE); if (DocUtil.isSharedDocument(doc)) { editorKit.initPlutextClient(editorView); } return editorView; } JComponent createMainPanel() { _desktop = new JDesktopPane(); _desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); _desktop.setBackground(Color.LIGHT_GRAY); JPanel toolbar = FxScriptUIHelper.getInstance().createToolBar(); JPanel panel = new JPanel(new BorderLayout()); panel.add(toolbar, BorderLayout.NORTH); panel.add(_desktop, BorderLayout.CENTER); panel.setBorder(new EmptyBorder(0, 2, 2, 2)); // top, left, bottom, right panel.setPreferredSize(new Dimension(640, 480)); return panel; } private int showConfirmClosingInternalFrame(JInternalFrame iframe, String resourceKeyPrefix) { int answer = JOptionPane.CANCEL_OPTION; String filePath = (String) iframe.getClientProperty(WordMLDocument.FILE_PATH_PROPERTY); ResourceMap rm = getContext().getResourceMap(); String title = rm.getString(resourceKeyPrefix + ".dialog.title") + " " + filePath.substring(filePath.lastIndexOf(File.separator) + 1); String message = filePath + "\n" + rm.getString(resourceKeyPrefix + ".confirmMessage"); Object[] options = { rm.getString(resourceKeyPrefix + ".confirm.saveNow"), rm.getString(resourceKeyPrefix + ".confirm.dontSave"), rm.getString(resourceKeyPrefix + ".confirm.cancel") }; answer = showConfirmDialog(title, message, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, options, options[0]); if (answer == JOptionPane.CANCEL_OPTION) { ; } else if (answer == JOptionPane.YES_OPTION) { boolean success = FileMenu.getInstance().save(iframe, null, FileMenu.SAVE_FILE_ACTION_NAME); if (success) { getToolbarStates().setDocumentDirty(iframe, false); getToolbarStates().setLocalEditsEnabled(iframe, false); } else { answer = JOptionPane.CANCEL_OPTION; } } else { //getToolbarStates().setDocumentDirty(iframe, false); } return answer; } private ViewManager getCurrentViewManager() { _viewManager.setOwner(getCurrentInternalFrame()); return _viewManager; } JMenuBar createMenuBar() { JMenuBar menubar = new JMenuBar(); JMenu fileMenu = FileMenu.getInstance().createJMenu(); JMenu editMenu = EditMenu.getInstance().createJMenu(); JMenu formatMenu = FormatMenu.getInstance().createJMenu(); JMenu hyperlinkMenu = HyperlinkMenu.getInstance().createJMenu(); JMenu contentControlMenu = ContentControlMenu.getInstance().createJMenu(); JMenu reviewMenu = ReviewMenu.getInstance().createJMenu(); JMenu viewMenu = ViewMenu.getInstance().createJMenu(); JMenu plutextMenu = PlutextMenu.getInstance().createJMenu(); JMenu windowMenu = WindowMenu.getInstance().createJMenu(); JMenu helpMenu = HelpMenu.getInstance().createJMenu(); menubar.add(fileMenu); menubar.add(Box.createRigidArea(new Dimension(12, 0))); menubar.add(editMenu); menubar.add(Box.createRigidArea(new Dimension(12, 0))); menubar.add(formatMenu); menubar.add(Box.createRigidArea(new Dimension(12, 0))); menubar.add(hyperlinkMenu); menubar.add(Box.createRigidArea(new Dimension(12, 0))); menubar.add(contentControlMenu); menubar.add(Box.createRigidArea(new Dimension(12, 0))); menubar.add(reviewMenu); menubar.add(Box.createRigidArea(new Dimension(12, 0))); menubar.add(viewMenu); menubar.add(Box.createRigidArea(new Dimension(12, 0))); menubar.add(plutextMenu); menubar.add(Box.createRigidArea(new Dimension(12, 0))); menubar.add(windowMenu); menubar.add(Box.createRigidArea(new Dimension(12, 0))); menubar.add(helpMenu); return menubar; } private class TitleBarMouseListener extends MouseMotionAdapter { public void mouseMoved(MouseEvent e){ JComponent titlePane = (JComponent) e.getSource(); JInternalFrame frame = (JInternalFrame) titlePane.getParent(); String tmp = frame.getTitle(); FontMetrics fm = frame.getFontMetrics(frame.getFont()); int width = fm.charsWidth(tmp.toCharArray(), 0, tmp.length()); Rectangle tbounds = titlePane.getBounds(); tbounds.width = width; if (tbounds.contains(e.getX(), e.getY())) { if (!tmp.startsWith(getUntitledFileName())) { tmp = (String) frame.getClientProperty(WordMLDocument.FILE_PATH_PROPERTY); //do not display password tmp = VFSUtils.getFriendlyName(tmp); } titlePane.setToolTipText(tmp); } else { titlePane.setToolTipText(null); } } } private class InternalFrameListener extends InternalFrameAdapter { public void internalFrameIconified(InternalFrameEvent e) { // Sets JInternalFrame's maximum property to false. // // When a user clicks the minimize/maximize button of // JInternalFrame, its maximum property value remains // unchanged. This subsequently causes // JInternalFrame.setMaximum() not working. JInternalFrame frame = (JInternalFrame) e.getSource(); try { frame.setMaximum(false); } catch (PropertyVetoException exc) { ;// do nothing } } public void internalFrameDeiconified(InternalFrameEvent e) { // Sets JInternalFrame's maximum property to false. // // When a user clicks the minimize/maximize button of // JInternalFrame, its maximum property value remains // unchanged. This subsequently causes // JInternalFrame.setMaximum() not working. JInternalFrame frame = (JInternalFrame) e.getSource(); try { frame.setMaximum(true); } catch (PropertyVetoException exc) { ;//do nothing } } public void internalFrameOpened(InternalFrameEvent e) { JInternalFrame iframe = (JInternalFrame) e.getSource(); WindowMenu.getInstance().addWindowMenuItem(iframe); } public void internalFrameClosing(InternalFrameEvent e) { JInternalFrame iframe = (JInternalFrame) e.getSource(); boolean canClose = true; if (getToolbarStates().isDocumentDirty(iframe)) { int answer = showConfirmClosingInternalFrame(iframe, "internalframe.close"); canClose = (answer != JOptionPane.CANCEL_OPTION); } if (canClose) { WordMLTextPane editor = SwingUtil.getWordMLTextPane(iframe); if (editor != null) { editor.removeCaretListener(getToolbarStates()); editor.removeFocusListener(getToolbarStates()); editor.setTransferHandler(null); editor.getDocument().removeDocumentListener(getToolbarStates()); WordMLEditorKit editorKit = (WordMLEditorKit) editor.getEditorKit(); editorKit.removeInputAttributeListener(getToolbarStates()); editor.getWordMLEditorKit().deinstall(editor); } iframe.dispose(); } } public void internalFrameClosed(InternalFrameEvent e) { JInternalFrame iframe = (JInternalFrame) e.getSource(); String filePath = (String) iframe.getClientProperty(WordMLDocument.FILE_PATH_PROPERTY); _iframeMap.remove(filePath); WindowMenu.getInstance().removeWindowMenuItem(iframe); _desktop.remove(iframe) ; } public void internalFrameActivated(InternalFrameEvent e) { JInternalFrame iframe = (JInternalFrame) e.getSource(); WindowMenu.getInstance().selectWindowMenuItem(iframe); } public void internalFrameDeactivated(InternalFrameEvent e) { JInternalFrame iframe = (JInternalFrame) e.getSource(); WindowMenu.getInstance().unSelectWindowMenuItem(iframe); } } //InternalFrameListener inner class private class ViewChangeListener implements ChangeListener { /* * On View tab changes this method checks whether * Editor View's content needs to be synchronized with * that of Source View's */ public void stateChanged(ChangeEvent event) { JTabbedPane pane = (JTabbedPane) event.getSource(); int editorViewIdx = pane.indexOfTab(getEditorViewTabTitle()); int sourceViewIdx = pane.indexOfTab(getSourceViewTabTitle()); int historyViewIdx = pane.indexOfTab(getContentControlHistoryViewTabTitle()); int recentViewIdx = pane.indexOfTab(getRecentChangesViewTabTitle()); if (pane.getSelectedIndex() == editorViewIdx && editorViewIdx != -1) { changeToEditorView(pane, editorViewIdx, sourceViewIdx); } else if (pane.getSelectedIndex() == sourceViewIdx && sourceViewIdx != -1 ) { changeToSourceView(pane, editorViewIdx, sourceViewIdx); } else if (pane.getSelectedIndex() == historyViewIdx && historyViewIdx != -1) { changeToContentControlHistoryView(pane, editorViewIdx, historyViewIdx); } else if (pane.getSelectedIndex() == recentViewIdx && recentViewIdx != -1) { changeToRecentChangesView(pane, editorViewIdx, recentViewIdx); } } private void changeToEditorView( JTabbedPane pane, int editorViewIdx, int sourceViewIdx) { if (sourceViewIdx == -1) { //No Source View return; } final WordMLTextPane editorView = (WordMLTextPane) SwingUtil.getDescendantOfClass( WordMLTextPane.class, (Container) pane.getComponentAt(editorViewIdx), true); final JEditorPane sourceView = (JEditorPane) SwingUtil.getDescendantOfClass( JEditorPane.class, (Container) pane.getComponentAt(sourceViewIdx), false); Boolean isSynched = (Boolean) sourceView.getClientProperty( Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG); if (!isSynched.booleanValue()) { //means that source view has been edited and //editorView has to be synchronised with source view. synchEditorView(editorView, sourceView); } SwingUtilities.invokeLater(new Runnable() { public void run() { editorView.requestFocusInWindow(); } }); } private void changeToSourceView( JTabbedPane pane, int editorViewIdx, int sourceViewIdx) { if (editorViewIdx == -1) { //No Editor View. //This should not happen. throw new IllegalStateException("No Editor View"); } final WordMLTextPane editorView = (WordMLTextPane) SwingUtil.getDescendantOfClass( WordMLTextPane.class, (Container) pane.getComponentAt(editorViewIdx), true); final JEditorPane sourceView = (JEditorPane) SwingUtil.getDescendantOfClass( JEditorPane.class, (Container) pane.getComponentAt(sourceViewIdx), false); Boolean isSynched = (Boolean) editorView.getClientProperty( Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG); if (!isSynched.booleanValue()) { synchSourceView(sourceView, editorView); } SwingUtilities.invokeLater(new Runnable() { public void run() { sourceView.requestFocusInWindow(); } }); } private void changeToContentControlHistoryView( JTabbedPane pane, int editorViewIdx, int versionHistoryViewIdx) { if (editorViewIdx == -1) { //No Editor View. //This should not happen. throw new IllegalStateException("No Editor View"); } final WordMLTextPane editorView = (WordMLTextPane) SwingUtil.getDescendantOfClass( WordMLTextPane.class, (Container) pane.getComponentAt(editorViewIdx), true); final JEditorPane historyView = (JEditorPane) SwingUtil.getDescendantOfClass( JEditorPane.class, (Container) pane.getComponentAt(versionHistoryViewIdx), false); synchContentControlHistoryView(historyView, editorView); SwingUtilities.invokeLater(new Runnable() { public void run() { historyView.requestFocusInWindow(); } }); } private void changeToRecentChangesView( JTabbedPane pane, int editorViewIdx, int recentChangesViewIdx) { if (editorViewIdx == -1) { //No Editor View. //This should not happen. throw new IllegalStateException("No Editor View"); } final WordMLTextPane editorView = (WordMLTextPane) SwingUtil.getDescendantOfClass( WordMLTextPane.class, (Container) pane.getComponentAt(editorViewIdx), true); final JEditorPane recentView = (JEditorPane) SwingUtil.getDescendantOfClass( JEditorPane.class, (Container) pane.getComponentAt(recentChangesViewIdx), false); synchRecentChangesView(recentView, editorView); SwingUtilities.invokeLater(new Runnable() { public void run() { recentView.requestFocusInWindow(); } }); } private void synchEditorView(WordMLTextPane editorView, JEditorPane sourceView) { int caretPos = editorView.getCaretPosition(); WordMLDocument editorViewDoc = (WordMLDocument) editorView.getDocument(); EditorKit kit = sourceView.getEditorKit(); AbstractDocument sourceViewDoc = (AbstractDocument) sourceView.getDocument(); try { sourceViewDoc.readLock(); editorViewDoc.lockWrite(); //Firstly, save source view content into WordprocessingMLPackage WordprocessingMLPackage wmlPackage = (WordprocessingMLPackage) sourceViewDoc.getProperty( WordMLDocument.WML_PACKAGE_PROPERTY); DocUtil.write(kit, sourceViewDoc, wmlPackage); //Now editorView's content has become invalid because //its WordprocessingMLPackage's main document part was //updated by DocUtil.write() above. //Need to refresh editor view. org.docx4j.wml.Document wmlDoc = (org.docx4j.wml.Document) wmlPackage.getMainDocumentPart().getJaxbElement(); editorViewDoc.replaceBodyML(new BodyML(wmlDoc.getBody())); log.debug("stateChanged(): NEW Document Structure..."); DocUtil.displayStructure(editorViewDoc); //reset LOCAL_VIEWS_SYNCHRONIZED_FLAG of source view sourceView.putClientProperty(Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG, Boolean.TRUE); } finally { editorViewDoc.unlockWrite(); sourceViewDoc.readUnlock(); editorView.validate(); editorView.repaint(); editorView.setCaretPosition(caretPos); } } private void synchSourceView(JEditorPane sourceView, WordMLTextPane editorView) { int caretPos = sourceView.getCaretPosition(); JEditorPane newView = createSourceView(editorView); Document newDoc = newView.getDocument(); sourceView.setDocument(newDoc); sourceView.validate(); sourceView.repaint(); sourceView.setCaretPosition(caretPos); //reset LOCAL_VIEWS_SYNCHRONIZED_FLAG of editor view editorView.putClientProperty(Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG, Boolean.TRUE); } private void synchContentControlHistoryView(JEditorPane historyView, WordMLTextPane editorView) { int caretPos = historyView.getCaretPosition(); JEditorPane newView = createContentControlHistoryView(editorView); Document newDoc = newView.getDocument(); historyView.setDocument(newDoc); historyView.validate(); historyView.repaint(); historyView.setCaretPosition(caretPos); } private void synchRecentChangesView(JEditorPane recentView, WordMLTextPane editorView) { int caretPos = recentView.getCaretPosition(); JEditorPane newView = createRecentChangesView(editorView); Document newDoc = newView.getDocument(); recentView.setDocument(newDoc); recentView.validate(); recentView.repaint(); recentView.setCaretPosition(caretPos); } } //ViewChangeListener inner class private class WmlExitListener implements ExitListener { public boolean canExit(EventObject event) { boolean cancelExit = false; if (getToolbarStates().isAnyDocumentDirty()) { List<JInternalFrame> list = getAllInternalFrames(); //Start from current editor's frame JInternalFrame currentFrame = getCurrentInternalFrame(); list.remove(currentFrame); list.add(0, currentFrame); for (JInternalFrame iframe: list) { if (getToolbarStates().isDocumentDirty(iframe)) { try { iframe.setSelected(true); iframe.setIcon(false); } catch (PropertyVetoException exc) { ;//ignore } int answer = showConfirmClosingInternalFrame( iframe, "Application.exit.saveFirst"); if (answer == JOptionPane.CANCEL_OPTION) { cancelExit = true; break; } } } //for (iframe) loop } //if (getToolbarStates().isAnyDocumentDirty() boolean canExit = false; if (!cancelExit) { ResourceMap rm = getContext().getResourceMap(); String title = rm.getString("Application.exit.dialog.title"); String message = rm.getString("Application.exit.confirmMessage"); int answer = showConfirmDialog( title, message, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); canExit = (answer == JOptionPane.YES_OPTION); }//if (!canExit) return canExit; } //canExit() public void willExit(EventObject event) { ;//not implemented } }//WMLExitListener inner class private class ViewManager { private JInternalFrame owner; private ViewChangeListener viewChangeListener; ViewManager() { this.viewChangeListener = new ViewChangeListener(); this.owner = null; } void setOwner(JInternalFrame iframe) { owner = iframe; } JTabbedPane getJTabbedPane() { checkOwner(); if (owner.getContentPane().getComponent(0) instanceof JTabbedPane) { return (JTabbedPane) owner.getContentPane().getComponent(0); } return null; } JEditorPane getEditorView() { checkOwner(); return getView(getEditorViewTabTitle()); } JEditorPane getSourceView() { checkOwner(); return getView(getSourceViewTabTitle()); } JEditorPane getVersionHistoryView() { checkOwner(); return getView(getContentControlHistoryViewTabTitle()); } JEditorPane getRecentChangesView() { checkOwner(); return getView(getRecentChangesViewTabTitle()); } private JEditorPane getView(String viewTabTitle) { JEditorPane theView = null; JTabbedPane tabbedPane = getJTabbedPane(); if (tabbedPane == null) { if (getEditorViewTabTitle().equals(viewTabTitle)) { theView = SwingUtil.getWordMLTextPane(owner); } else { //There should not be any other view. //Other views are always created in a Tabbed pane. theView = null; } } else { int idx = tabbedPane.indexOfTab(viewTabTitle); if (idx != -1) { theView = (JEditorPane) SwingUtil.getDescendantOfClass( JEditorPane.class, (Container) tabbedPane.getComponentAt(idx), false); } } return theView; } void showViewTab(String tabTitle) { checkOwner(); WordMLTextPane editorView = (WordMLTextPane) getEditorView(); if (editorView == null) { throw new IllegalStateException("No Editor View"); } JTabbedPane tabbedPane = getJTabbedPane(); int tabIdx = -1; if (tabbedPane != null) { tabIdx = tabbedPane.indexOfTab(tabTitle); } JEditorPane view = null; if (tabIdx == -1) { //Add view tab if (getSourceViewTabTitle().equals(tabTitle)) { view = createSourceView(editorView); addViewTab(view, tabTitle); view.setCaretPosition(0); } else if (getContentControlHistoryViewTabTitle().equals(tabTitle)) { view = createContentControlHistoryView(editorView); addViewTab(view, tabTitle); view.setCaretPosition(0); } else if (getRecentChangesViewTabTitle().equals(tabTitle)) { view = createRecentChangesView(editorView); addViewTab(view, tabTitle); view.setCaretPosition(0); } } else { tabbedPane.setSelectedIndex(tabIdx); view = (JEditorPane) SwingUtil.getDescendantOfClass( JEditorPane.class, (Container) tabbedPane.getComponentAt(tabIdx), false); } if (view != null) { final JEditorPane ep = view; SwingUtilities.invokeLater(new Runnable() { public void run() { ep.requestFocusInWindow(); } }); } } void closeViewTab(String tabTitle) { removeViewTab(tabTitle); } void removeViewTab(String tabTitle) { checkOwner(); final JTabbedPane tabbedPane = getJTabbedPane(); if (tabbedPane == null) { return; } if (getEditorViewTabTitle().equals(tabTitle)) { //Editor View should not be closed throw new IllegalArgumentException("Tab title=" + tabTitle); } int idx = tabbedPane.indexOfTab(getEditorViewTabTitle()); if (idx == -1) { throw new IllegalStateException("No Editor View Tab"); } //Disable tabbedPane's change listener. //This is needed to stop the view synchronisation process in it. tabbedPane.removeChangeListener(this.viewChangeListener); if (tabbedPane.getTabCount() > 2) { idx = tabbedPane.indexOfTab(tabTitle); tabbedPane.removeTabAt(idx); int lastTab = tabbedPane.getTabCount() - 1; tabbedPane.setSelectedIndex(lastTab); //Put change listener back on. tabbedPane.addChangeListener(ViewManager.this.viewChangeListener); final JEditorPane viewToFocus = (JEditorPane) SwingUtil.getDescendantOfClass( JEditorPane.class, (Container) tabbedPane.getComponentAt(lastTab), false); SwingUtilities.invokeLater(new Runnable() { public void run() { viewToFocus.requestFocusInWindow(); } }); } else if (tabbedPane.getTabCount() == 2) { //If Tabbed pane consists of Editor View and the view being closed JPanel viewPanel = (JPanel) tabbedPane.getComponentAt(idx); Rectangle bounds = owner.getBounds(); owner.getContentPane().removeAll(); //Move editorViewPanel from tabbedPane to owner's content tabbedPane.remove(idx); owner.getContentPane().add(viewPanel); owner.invalidate(); owner.validate(); owner.setBounds(bounds); final JEditorPane viewToFocus = (JEditorPane) SwingUtil.getDescendantOfClass( JEditorPane.class, (Container) viewPanel, false); SwingUtilities.invokeLater(new Runnable() { public void run() { viewToFocus.requestFocusInWindow(); } }); } else { //should never happen. } } //removeViewTab() void selectViewTab(String tabTitle) { JTabbedPane tabbedPane = getJTabbedPane(); if (tabbedPane == null) { throw new IllegalStateException("No Tabbed Pane."); } int idx = tabbedPane.indexOfTab(tabTitle); if (idx != -1) { tabbedPane.setSelectedIndex(idx); } } private void addViewTab(final JEditorPane view, String tabTitle) { JPanel viewPanel = FxScriptUIHelper.getInstance().createEditorPanel(view); Rectangle bounds = owner.getBounds(); JTabbedPane tabbedPane = getJTabbedPane(); if (tabbedPane == null) { //Create Tabbed Pane. //ADD Editor View Tab and new 'viewPanel' Tab JPanel editorViewPanel = (JPanel) owner.getContentPane().getComponent(0); owner.getContentPane().removeAll(); tabbedPane = new JTabbedPane(); tabbedPane.addTab(getEditorViewTabTitle(), editorViewPanel); tabbedPane.addTab(tabTitle, viewPanel); owner.getContentPane().add(tabbedPane); owner.invalidate(); owner.validate(); } else { //Disable tabbedPane's change listener. //This is needed to avoid unnecessary view synchronisation //process due to tab addition. tabbedPane.removeChangeListener(this.viewChangeListener); //Add Tab tabbedPane.addTab(tabTitle, viewPanel); tabbedPane.invalidate(); tabbedPane.validate(); } int i = tabbedPane.indexOfTab(tabTitle); tabbedPane.setSelectedIndex(i); //Put change listener back on tabbedPane.addChangeListener(this.viewChangeListener); owner.setBounds(bounds); } private void checkOwner() { if (owner == null) { throw new IllegalStateException("No Owner"); } } } //TabbedPaneManager inner class }// WordMLEditor class